using Microsoft.Extensions.Logging;
using NModbus;
using RotovapPlatform.Domain.Common.Exceptions;
using RotovapPlatform.Domain.Equipment.Configurations;
using RotovapPlatform.Domain.Equipment.Interfaces.Communication;
using RotovapPlatform.Infrastructure.Equipment.Devices.Robot;
using System.Net.Sockets;

namespace RotovapPlatform.Infrastructure.Equipment.Communication.Modbus;

/// <summary>
/// Modbus TCP通信客户端
/// </summary>
public class ModbusTcpClient : IModbusTcpClient
{
	private readonly ModbusTcpConfig _config;
	private readonly ILogger<ModbusTcpClient> _logger;
	private IModbusMaster? _master;
	private TcpClient? _client;
	private bool _isConnected;
	private readonly object _connectionLock = new();
	private readonly SemaphoreSlim _semaphore = new(1, 1);

	public bool IsConnected
	{
		get
		{
			lock (_connectionLock)
			{
				return _isConnected;
			}
		}
	}

	public IModbusMaster? Master => _master;

	/// <summary>
	/// 构造函数
	/// </summary>
	/// <param name="config">Modbus TCP配置</param>
	/// <param name="logger">日志记录器</param>
	public ModbusTcpClient(ModbusTcpConfig config, ILogger<ModbusTcpClient> logger)
	{
		_config = config;
		_logger = logger;
	}

	/// <summary>
	/// 设置连接断开状态，释放相关资源
	/// </summary>
	private void SetDisconnected()
	{
		lock (_connectionLock)
		{
			_isConnected = false;
			_master?.Dispose();
			_master = null;
			_client?.Dispose();
			_client = null;
		}
	}

	/// <summary>
	/// 建立Modbus TCP连接
	/// </summary>
	/// <returns>连接是否成功</returns>
	public async Task<bool> ConnectAsync()
	{
		try
		{
			lock (_connectionLock)
			{
				if (_isConnected) return true;

				_client = new TcpClient(_config.IpAddress, _config.Port);
				_master = new ModbusFactory().CreateMaster(_client);
				_isConnected = true;
			}
			return true;
		}
		catch
		{
			lock (_connectionLock)
			{
				_isConnected = false;
				_master = null; // 确保状态与实例同步
			}
			return false;
		}
	}

	/// <summary>
	/// 确保连接可用，如果断开则尝试重连
	/// </summary>
	/// <returns>连接是否可用</returns>
	private async Task<bool> EnsureConnectedAsync(byte slaveId, ushort startAddress, ushort length)
	{
		try
		{
			if (_master == null)
			{
				_logger.LogWarning("Modbus主站实例未初始化");
				return false;
			}

			await _semaphore.WaitAsync();
			if (!_isConnected)
			{
				_client = new TcpClient();
				using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
				var connectTask = _client.ConnectAsync(_config.IpAddress, _config.Port);
				await connectTask.WaitAsync(cts.Token);

				var factory = new ModbusFactory();
				_master = factory.CreateMaster(_client);
				_isConnected = true;
				_logger.LogInformation("Modbus TCP连接成功");
			}
			return true;
		}
		catch (Exception ex)
		{
			_logger.LogError(ex, "Modbus TCP连接失败");
			SetDisconnected();
			throw new CommunicationException("建立连接失败", ex);
		}
		finally
		{
			_semaphore.Release();
		}
	}

	/// <summary>
	/// 读取输入寄存器
	/// </summary>
	/// <param name="startAddress">起始地址</param>
	/// <param name="length">读取长度</param>
	/// <returns>读取的数据，如果失败返回空数组</returns>
	public async Task<ushort[]> ReadInputRegistersAsync(byte slaveId, ushort startAddress, ushort length)
	{
		try
		{
			if (!await EnsureConnectedAsync(slaveId, startAddress, length))
			{
				SetDisconnected();
				throw new CommunicationException("Modbus TCP连接失败");
			}

			return await _master.ReadInputRegistersAsync(slaveId, startAddress, length);
		}
		catch (Exception ex)
		{
			_logger.LogError(ex, "读取输入寄存器失败");
			SetDisconnected();
			throw new CommunicationException("读取输入寄存器失败", ex);
		}
	}

	/// <summary>
	/// 读取离散输入
	/// </summary>
	/// <param name="startAddress">起始地址</param>
	/// <param name="length">读取长度</param>
	/// <returns>读取的数据，如果失败返回空数组</returns>
	public async Task<bool[]> ReadDiscreteInputsAsync(byte slaveId, ushort startAddress, ushort length)
	{
		try
		{
			if (!await EnsureConnectedAsync(slaveId, startAddress, length))
			{
				SetDisconnected();
				throw new CommunicationException("Modbus TCP连接失败");
			}

			return await _master.ReadInputsAsync(slaveId, startAddress, length);
		}
		catch (Exception ex)
		{
			_logger.LogError(ex, "读取离散输入失败");
			SetDisconnected();
			throw new CommunicationException("读取离散输入失败", ex);
		}
	}

	/// <summary>
	/// 写入多个线圈
	/// </summary>
	/// <param name="slaveId">从站ID</param>
	/// <param name="startAddress">起始地址</param>
	/// <param name="values">要写入的值</param>
	/// <returns>是否写入成功</returns>
	public async Task<bool> WriteMultipleCoilsAsync(byte slaveId, ushort startAddress, bool[] values)
	{
		if (_master == null)
		{
			_logger.LogError("无法执行写操作：Modbus主站未初始化");
			return false;
		}

		try
		{
			await _master.WriteMultipleCoilsAsync(slaveId, startAddress, values);
			return true;
		}
		catch (Exception ex)
		{
			_logger.LogError(ex, "写入多个线圈失败");
			SetDisconnected();
			return false;
		}
	}

	/// <summary>
	/// 写入多个保持寄存器
	/// </summary>
	/// <param name="startAddress">起始地址</param>
	/// <param name="data">要写入的数据</param>
	/// <returns>是否写入成功</returns>
	public async Task<bool> WriteMultipleRegistersAsync(byte slaveId, ushort startAddress, ushort[] data)
	{
		try
		{
			await _master.WriteMultipleRegistersAsync(slaveId, startAddress, data);
			return true;
		}
		catch (Exception ex)
		{
			_logger.LogError(ex, "写入保持寄存器失败");
			SetDisconnected();
			return false;
		}
	}

	/// <summary>
	/// 释放资源
	/// </summary>
	public void Dispose()
	{
		SetDisconnected();
	}

	public async Task DisconnectAsync()
	{
		lock (_connectionLock)
		{
			if (!_isConnected) return;

			_master?.Dispose();
			_isConnected = false;
		}
	}
}

